package han.chensing.latos.maker.windows.widget.simulator;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Widget;
import han.chensing.latos.common.level.content.objects.CommonLine;
import han.chensing.latos.common.level.content.objects.CommonNote;
import han.chensing.latos.common.level.content.timeLine.frame.FrameGeneral;
import han.chensing.latos.common.level.util.FrameUtil;
import han.chensing.latos.common.level.util.MathUtil;
import han.chensing.latos.maker.GameMain;
import han.chensing.latos.maker.V;
import han.chensing.latos.maker.res.Res;
import space.earlygrey.shapedrawer.ShapeDrawer;

import java.util.ArrayList;

/**
 * 造他就完了
 */
public class LatosSimulatorWidget extends Widget {


    Color backgroundColor=new Color(0x1f1e33ff);
    final float defaultWidth=1920f;
    final float defaultHeight =1080f;

    TextureRegion tapTexture;
    TextureRegion dragTexture;
    TextureRegion flickTexture;

    protected final SimulatorMath worldMath =new SimulatorMath(defaultWidth,defaultHeight);
    protected SimulatorMath canvasMath;
    protected float screenMultiple;

    protected OrthographicCamera camera;
    protected float nowTime;

    float viewTx,viewTy;
    float dragTx,dragTy;
    float viewTZoom;

    @Override
    public void draw(Batch batch, float parentAlpha) {
        super.draw(batch, parentAlpha);
        camera.update();
        updateZoom();
        batch.flush();
        if (clipBegin(getX(),getY(),getWidth(),getHeight())) {
            float width = getWidth();
            float height = getHeight();
            float x = getX();
            float y = getY();
            ShapeDrawer shapeDrawer = GameMain.shapeDrawer;
            shapeDrawer.rectangle(x, y, width, height);
            drawBackground();
            drawBroad();
            drawLines(batch);
            batch.flush();
            clipEnd();
        }
    }

    protected void drawBackground(){
        ShapeDrawer shapeDrawer = GameMain.shapeDrawer;
        shapeDrawer.filledRectangle(getX(),getY(),getWidth(),getHeight(),backgroundColor);
    }

    protected void drawLines(Batch batch){
        ArrayList<CommonLine> commonLines = V.project.getLevelData().getLineObjects();
        for (CommonLine commonLine : commonLines) drawALineInCurrentFrame(batch,commonLine);
    }

    protected void drawALineInCurrentFrame(Batch batch,CommonLine commonLine){
        FrameGeneral lineNowFrame= modifyLineNowFrame(FrameUtil.getGeneralLineNowFrame(commonLine,nowTime));
        drawALine(commonLine,lineNowFrame,batch);
    }

    protected void drawALine(CommonLine commonLine, FrameGeneral lineNowFrame, Batch batch){
        drawLine(batch, commonLine,lineNowFrame);
        drawNotes(batch,commonLine,lineNowFrame);
    }

    protected void drawBroad(){
        ShapeDrawer shapeDrawer = GameMain.shapeDrawer;
        float x = getX();
        float y = getY();
        float[] canvasPos1 = project(new float[]{0, 0});
        float[] canvasPos2 = project(new float[]{defaultWidth,defaultHeight});
        shapeDrawer.rectangle(
                x+canvasPos1[0],
                y+canvasPos1[1],
                canvasPos2[0]-canvasPos1[0],
                canvasPos2[1]-canvasPos1[1],
                Color.GRAY
        );
    }

    protected void drawLine(Batch batch, CommonLine line, FrameGeneral lineNowFrame){
        BitmapFont fontConsolas = Res.get().get("fontDeng", BitmapFont.class);
        float x = getX();
        float y = getY();
        ShapeDrawer shapeDrawer = GameMain.shapeDrawer;
        float[] linePosWorld=new float[]{lineNowFrame.getX(),lineNowFrame.getY()};
        float[] linePosCanvas=project(linePosWorld);
        float lineXCanvas = linePosCanvas[0];
        float lineYCanvas = linePosCanvas[1];
        float lineRotate = lineNowFrame.getRotate();
        float[] crossPointWithScreen1 =
                canvasMath.getCrossPointWithScreen(
                        lineXCanvas,
                        lineYCanvas,
                        lineRotate);
        float[] crossPointWithScreen2 =
                canvasMath.getCrossPointWithScreen(
                        lineXCanvas,
                        lineYCanvas,
                        lineRotate+180);
        shapeDrawer.line(
                x+lineXCanvas,
                y+lineYCanvas,
                x+crossPointWithScreen1[0],
                y+crossPointWithScreen1[1],
                3);
        shapeDrawer.line(
                x+lineXCanvas,
                y+lineYCanvas,
                x+crossPointWithScreen2[0],
                y+crossPointWithScreen2[1],
                3);
        fontConsolas.draw(batch,line.getName(),x+lineXCanvas+3,y+lineYCanvas-5);
    }

    protected void drawNotes(Batch batch,CommonLine commonLine, FrameGeneral lineNowFrame){
        Color batchOriginalColor = new Color(batch.getColor());
        ArrayList<CommonNote> commonNotes = commonLine.getNotes();
        float x = getX();
        float y = getY();
        float time = lineNowFrame.getTime();
        float ranDistance = lineNowFrame.getSpeed()*time;
        for (CommonNote commonNote : commonNotes) {
            float distanceTime = commonNote.getStartTime();
            float distanceInWorld=distanceTime*lineNowFrame.getSpeed()-ranDistance;
            int backGoingFix = commonNote.isBackGoing() ? -1 : 1;
            distanceInWorld*=-backGoingFix;
            float lineX = lineNowFrame.getX();
            float lineY = lineNowFrame.getY();
            float distanceToLineCenterInWorld = commonNote.getDistanceToLineCenter();
            float hDistanceDegree = lineNowFrame.getRotate();
            float heightDegree =lineNowFrame.getRotate() - 90f;
            float[] posID2LC = worldMath.getPointInAwayFromALine(
                    lineX, lineY,
                    hDistanceDegree,
                    distanceToLineCenterInWorld-tapTexture.getRegionWidth()/2f);
            float[] pointAwayOfTheLine = worldMath.getPointInAwayFromALine(
                    posID2LC[0], posID2LC[1],
                    heightDegree, distanceInWorld+tapTexture.getRegionHeight()/2f);
            if (!isInBox(pointAwayOfTheLine)) continue;
            TextureRegion texture = selectSimpleNoteTexture(commonNote);
            if (texture==null) continue;
            Color noteColor = new Color(batch.getColor());
            noteColor.a=distanceInWorld*backGoingFix>0?0.4f:1f;
            float[] positionInCanvas = project(pointAwayOfTheLine);
            batch.setColor(noteColor);
            batch.draw(texture,
                    x+ positionInCanvas[0],
                    y+ positionInCanvas[1],
                    0,0,
                    texture.getRegionWidth()/camera.zoom,
                    texture.getRegionHeight()/camera.zoom,
                    1f, 1f,
                    lineNowFrame.getRotate());
        }
        batch.setColor(batchOriginalColor);
    }

    protected void updateZoom(){
        float delta = viewTZoom - camera.zoom;
        camera.zoom+=delta*15f* Gdx.graphics.getDeltaTime();
    }

    protected TextureRegion selectSimpleNoteTexture(CommonNote note){
        return switch (note.getNoteType()){
            case Tap -> tapTexture;
            case Drag -> dragTexture;
            case Flick -> flickTexture;
            default -> null;
        };
    }

    protected FrameGeneral modifyLineNowFrame(FrameGeneral lineNowFrame){
        return lineNowFrame;
    }

    protected boolean isInBox(float[] worldPos){
        float width = getWidth();
        float height = getHeight();
        int tapTextureHeight = tapTexture.getRegionHeight();
        int tapTextureWidth = tapTexture.getRegionWidth();
        float[] project = project(worldPos);
        return MathUtil.isInRange(-tapTextureWidth, project[0],width+ tapTextureWidth)
                &&MathUtil.isInRange(-tapTextureHeight, project[1],height+ tapTextureHeight);
    }

    protected float[] project(float[] xy){
        return project(xy[0],xy[1]);
    }

    protected float[] unproject(float[] cxy){
        return unproject(cxy[0],cxy[1]);
    }

    protected float[] project(float x,float y){
        Vector3 project = camera.project(
                new Vector3(x, y, 0),
                0,0,
                defaultWidth,defaultHeight);
        return new float[]{project.x,project.y};
    }

    protected float[] unproject(float cx,float cy){
        Vector3 unproject = camera.unproject(
                new Vector3(cx, cy, 0),
                0,0,
                defaultWidth,defaultHeight);
        return new float[]{unproject.x,unproject.y};
    }

    protected float timeToDistance(float time,float speed){
        return time*speed;
    }

    protected float[] getCanvasCenterInWorldPos(){
        return unproject(new float[]{getWidth()/2f,getHeight()/2f});
    }

    public void setNowTime(float nowTime) {
        this.nowTime = nowTime;
    }

    public void init(){
        tapTexture = Res.get().get("tap", TextureRegion.class);
        dragTexture = Res.get().get("drag", TextureRegion.class);
        flickTexture = Res.get().get("flick", TextureRegion.class);
        float width = getWidth();
        float height = getHeight();
        this.camera=new OrthographicCamera(defaultWidth,defaultHeight);
        this.canvasMath =new SimulatorMath(width,height);
        this.screenMultiple=(width/defaultWidth+height/defaultHeight)/2f;
        initCamera();
        addListener(new InputListener(){
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                dragTx=x;
                dragTy=y;
                viewTx=camera.position.x;
                viewTy=camera.position.y;
                Stage stage = getStage();
                if (stage!=null)
                    stage.setScrollFocus(LatosSimulatorWidget.this);
                return true;
            }
            @Override
            public void touchDragged(InputEvent event, float x, float y, int pointer) {
                super.touchDragged(event, x, y, pointer);
                camera.position.x=viewTx-(x-dragTx)*camera.zoom;
                camera.position.y=viewTy-(y-dragTy)*camera.zoom;
            }
            @Override
            public boolean scrolled(InputEvent event, float x, float y, float amountX, float amountY) {
                float rate = amountY* 0.2f;
                viewTZoom+=rate;
                float fixedMaxScale = 10 * screenMultiple;
                float fixedMinScale = 0.5f * screenMultiple;
                viewTZoom=MathUtil.range(fixedMinScale,viewTZoom, fixedMaxScale);
                return true;
            }
        });
    }

    private void initCamera(){
        camera.update();
        camera.position.set(defaultWidth/2f,defaultHeight/2f,0);
        camera.near=0;
        camera.far=0.1f;
        camera.normalizeUp();
        camera.direction.set(0,0,-1);
        camera.update();
        float[] canvasInWorldWidthAndHeight = unproject(getWidth(), getHeight());
        camera.zoom=defaultWidth/canvasInWorldWidthAndHeight[0];
        camera.update();
        camera.position.set(defaultWidth/2f*camera.zoom,defaultHeight/2f*camera.zoom,0);
        camera.update();
        viewTZoom=camera.zoom;
        camera.update();
    }
}
